﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace SBPweb.PROvibe.Shared.Utilities.Collections
{

    public class OverridableDictionary<TKey, TValue> : IOverridableDictionary<TKey, TValue>
    {

        #region Private fields

        private bool isInitializing = false;
        private List<DictionaryItemEventArgs<TKey, TValue>> eventQueueAdd = new List<DictionaryItemEventArgs<TKey,TValue>>();
        private List<DictionaryItemEventArgs<TKey, TValue>> eventQueueChange = new List<DictionaryItemEventArgs<TKey,TValue>>();
        private List<DictionaryItemEventArgs<TKey, TValue>> eventQueueRemove = new List<DictionaryItemEventArgs<TKey,TValue>>();
        protected Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>();
        protected Dictionary<TKey, Type> types = new Dictionary<TKey, Type>();

        #endregion

        #region Constructor logic

        public OverridableDictionary()
        {
        }

        #endregion

        #region Property accessors

        public ICollection<TKey> Keys
        {
            get
            {
                return dictionary.Keys;
            }
        }

        public ICollection<TValue> Values
        {
            get
            {
                return dictionary.Values;
            }
        }

        public ICollection<Type> Types
        {
            get
            {
                return types.Values;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                if (dictionary.ContainsKey(key))
                {
                    return dictionary[key];
                }
                return default(TValue);
            }
            set
            {
                bool b = dictionary.ContainsKey(key);
                dictionary[key] = value;
                if (value == null)
                {
                    types[key] = typeof(TValue);
                }
                else
                {
                    types[key] = value.GetType();
                }
                if (b)
                {
                    if (ItemChanged != null)
                    {
                        DictionaryItemEventArgs<TKey, TValue> diea = new DictionaryItemEventArgs<TKey, TValue>(key, value);
                        if (isInitializing)
                        {
                            lock (eventQueueChange)
                            {
                                if (!eventQueueChange.Contains(diea))
                                {
                                    eventQueueChange.Add(diea);
                                }
                            }
                        }
                        else
                        {
                            ItemChanged(this, diea);
                        }
                    }
                }
                else
                {
                    if (ItemAdded != null)
                    {
                        DictionaryItemEventArgs<TKey, TValue> diea = new DictionaryItemEventArgs<TKey, TValue>(key, value);
                        if (isInitializing)
                        {
                            lock (eventQueueAdd)
                            {
                                if (!eventQueueAdd.Contains(diea))
                                {
                                    eventQueueAdd.Add(diea);
                                }
                            }
                        }
                        else
                        {
                            ItemAdded(this, diea);
                        }
                    }
                }
            }
        }

        //public IEnumerable<TValue> Items
        //{
        //    get
        //    {
        //        return dict;
        //    }
        //    set
        //    {
        //        if (ItemRemoved != null)
        //        {
        //            for (int i = 0; i < dict.Count; i++)
        //            {
        //                ItemRemoved(this, new ItemEventArgs(dict[i], i));
        //            }
        //        }
        //        if (value is List<TItem>)
        //        {
        //            dict = value as List<TItem>;
        //        }
        //        else
        //        {
        //            dict = new List<TItem>(value);
        //        }
        //        if (ItemAdded != null)
        //        {
        //            for (int i = 0; i < dict.Count; i++)
        //            {
        //                ItemAdded(this, new ItemEventArgs(dict[i], i));
        //            }
        //        }
        //    }
        //}

        public int Count
        {
            get
            {
                return dictionary.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        #endregion

        #region Events

        public event EventHandler InitializationBegin;

        public event EventHandler InitializationEnd;

        public event EventHandler<DictionaryItemEventArgs<TKey, TValue>> ItemAdded;

        public event EventHandler<DictionaryItemEventArgs<TKey, TValue>> ItemChanged;

        public event EventHandler<DictionaryItemEventArgs<TKey, TValue>> ItemRemoved;

        #endregion

        #region Methods

        public virtual void Add(TKey key, TValue value)
        {
            dictionary.Add(key, value);

            if (value == null)
            {
                types.Add(key, typeof(TValue));
            }
            else
            {
                types.Add(key, value.GetType());
            }

            if (ItemAdded != null)
            {
                DictionaryItemEventArgs<TKey, TValue> diea = new DictionaryItemEventArgs<TKey, TValue>(key, value);
                if (isInitializing)
                {
                    lock (eventQueueAdd)
                    {
                        if (!eventQueueAdd.Contains(diea))
                        {
                            eventQueueAdd.Add(diea);
                        }
                    }
                }
                else
                {
                    ItemAdded(this, diea);
                }
            }
        }

        public virtual void Add(KeyValuePair<TKey, TValue> keyValuePair)
        {
            this.Add(keyValuePair.Key, keyValuePair.Value);
        }

        public virtual void Clear()
        {
            foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
            {
                Remove(kvp);
            }
            dictionary.Clear();
            types.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> keyValuePair)
        {
            if (!dictionary.ContainsKey(keyValuePair.Key))
            {
                return false;
            }
            if (!dictionary.ContainsValue(keyValuePair.Value))
            {
                return false;
            }
            return dictionary[keyValuePair.Key].Equals(keyValuePair.Value);
        }

        public bool ContainsKey(TKey key)
        {
            return dictionary.ContainsKey(key);
        }

        public bool ContainsValue(TValue value)
        {
            return dictionary.ContainsValue(value);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            int i = 0;
            foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
            {
                if(i++ >= arrayIndex)
                {
                    array[i + arrayIndex] = kvp;
                }
            }
        }

        public virtual bool Remove(TKey key)
        {
            TValue value = dictionary[key];
            return Remove(new KeyValuePair<TKey, TValue>(key, value));
        }

        public virtual bool Remove(KeyValuePair<TKey, TValue> keyValuePair)
        {
            bool ret = dictionary.Remove(keyValuePair.Key);
            ret &= types.Remove(keyValuePair.Key);
            if (ret && ItemRemoved != null)
            {
                DictionaryItemEventArgs<TKey, TValue> diea = new DictionaryItemEventArgs<TKey, TValue>(keyValuePair.Key, keyValuePair.Value);
                if (isInitializing)
                {
                    lock (eventQueueRemove)
                    {
                        if (!eventQueueRemove.Contains(diea))
                        {
                            eventQueueRemove.Add(diea);
                        }
                    }
                }
                else
                {
                    ItemRemoved(this, diea);
                }
            }
            return ret;
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return dictionary.TryGetValue(key, out value);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return dictionary.GetEnumerator();
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return dictionary.GetEnumerator();
        }

        public void BeginInitialization()
        {
            isInitializing = true;
            if (InitializationBegin != null)
            {
                InitializationBegin(this, EventArgs.Empty);
            }
        }

        public void EndInitialization()
        {
            ProcessEventQueues();
            isInitializing = false;
            if (InitializationEnd != null)
            {
                InitializationEnd(this, EventArgs.Empty);
            }
        }

        private void ProcessEventQueues()
        {
            lock (eventQueueAdd)
            {
                foreach (DictionaryItemEventArgs<TKey, TValue> e in eventQueueAdd)
                {
                    ItemAdded(this, e);
                }
                eventQueueAdd.Clear();
            }
            lock (eventQueueChange)
            {
                foreach (DictionaryItemEventArgs<TKey, TValue> e in eventQueueChange)
                {
                    ItemChanged(this, e);
                }
                eventQueueChange.Clear();
            }
            lock (eventQueueRemove)
            {
                foreach (DictionaryItemEventArgs<TKey, TValue> e in eventQueueRemove)
                {
                    ItemRemoved(this, e);
                }
                eventQueueRemove.Clear();
            }
        }

        #endregion

    }

}